// 版权所有2012 Go作者。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// 包类型声明数据类型并实现
// Go包的类型检查算法。使用
// 配置。选中以调用包的类型检查器。
// 或者，使用NewChecker创建一个新的类型检查器
// 并通过调用checker递增地调用它。文件夹。
// 
// 类型检查由几个相互依赖的阶段组成：
// 
// 名称解析将程序中的每个标识符（syntax.Name）映射到它所表示的
// 语言对象（object）。
// 使用信息。{Defs，Uses，Implicits}用于名称解析的结果。
// 
// 常数折叠为编译时常数的每个表达式（syntax.Expr）计算精确的常量值（Constant.value）
// 。
// 使用信息。类型[expr]。恒定折叠结果的值。
// 
// 类型推断计算每个表达式（syntax.Expr）的类型（Type）
// 并检查是否符合语言规范。
// 使用信息。类型[expr]。用于类型推断结果的类型。
// 
package types2

import (
	"bytes"
	"cmd/compile/internal/syntax"
	"fmt"
	"go/constant"
)

// 错误描述类型检查错误；它实现了错误接口。
// “软”错误是指仍然允许对
// 包（例如“未使用的变量”）进行有效解释的错误；如果忽略“硬”错误，可能会导致不可预测的
// 行为。
type Error struct {
	Pos  syntax.Pos // 错误位置
	Msg  string     // 默认错误消息，用户友好的
	Full string     // 完整错误消息，用于调试（可能包含内部详细信息）
	Soft bool       // 如果设置，error is“soft”
}

// error返回一个错误字符串，格式如下：
// 文件名：line:column:message 
func (err Error) Error() string {
	return fmt.Sprintf("%s: %s", err.Pos, err.Msg)
}

// FullError返回一个类似error的错误字符串，它可能包含
// 类型检查器内部详细信息，如
// 参数的下标索引等。用于调试。
func (err Error) FullError() string {
	return fmt.Sprintf("%s: %s", err.Pos, err.Full)
}

// ArgumentError包含与参数索引关联的错误。
type ArgumentError struct {
	Index int
	Err   error
}

func (e *ArgumentError) Error() string { return e.Err.Error() }
func (e *ArgumentError) Unwrap() error { return e.Err }

// 导入程序解析到包的导入路径。
// 
// 注意：此接口不支持导入本地
// 卖方软件包。看见https:
// 如果可能，外部实现应该实现ImporterFrom。
type Importer interface {
	// 导入返回给定导入路径的导入包。
	// 语义类似于ImporterFrom。ImportFrom，但忽略
	// 目录和模式（因为它们不存在）。
	Import(path string) (*Package, error)
}

// ImportMode保留供将来使用。
type ImportMode int

// ImporterFrom解析到包的导入路径；它
// 支持按https:
// 使用go/importer从实现中获取importer。
type ImporterFrom interface {
	// 存在导入程序以实现向后兼容性。调用
	// Import（路径）与调用ImportFrom（路径“，”0）相同；
	// 也就是说，可能找不到本地出售的软件包。
	// 如果存在来自
	// 的ImporterFrom，则types包不会调用Import。
	Importer

	// ImportFrom通过位于目录中的包文件导入时，返回给定导入路径的导入包。
	// 如果导入失败，除了返回一个错误外，如果创建了一个
	// 缓存并返回一个包。这将减少由于缺少包而导致的包不一致和
	// 文件包，建议ImportFrom 
	// 后续类型检查器错误。
	// 模式值必须为0；保留供将来使用。
	// 使用相同路径和目录对ImportFrom的两个调用必须返回相同的包。
	ImportFrom(path, dir string, mode ImportMode) (*Package, error)
}

// 配置指定类型检查的配置。
// 配置的零值是一个随时可用的默认配置。
type Config struct {
	// 上下文是用于解析全局标识符的上下文。如果为nil，
	// 类型检查器将使用新创建的上下文初始化此字段。
	Context *Context

	// GoVersion描述了公认的围棋语言版本。字符串
	// 必须遵循格式“go%d.%d”（例如“go1.12”）或ist必须为
	// 空；空字符串表示最新的语言版本。
	// 如果格式无效，调用类型检查器将导致
	// 恐慌。
	GoVersion string

	// 如果设置了IgnoreFuncBody，则函数体不是
	// 类型已检查。
	IgnoreFuncBodies bool

	// 如果设置了FakeImportC，`import“C”`（对于需要Cgo的包）
	// 声明一个空的“C”包，对于符合条件的
	// 标识符引用包C（找不到对象）。
	// 此功能适用于标准库cmd/api工具。
	// 
	// 注意：由于后续错误，影响可能不可预测。
	// 不要随意使用！
	FakeImportC bool

	// 如果设置了IgnoreLabels，则不会检查正确的标签使用。
	// TODO（gri）合并标签检查并删除此标志。
	IgnoreLabels bool

	// 如果设置了CompilerErrorMessages，则使用
	// cmd/compile error字符串报告错误，以匹配$GOROOT/test errors。
	// TODO（gri）合并错误消息并删除此标志。
	CompilerErrorMessages bool

	// 如果设置了go115UsesCgo，则类型检查器需要
	// /\u cgo\u GOTYPE。运行cmd/cgo生成的go文件是
	// 作为包源文件提供。限定标识符
	// 参考包C将解析为cgo提供的
	// 在_cgo_gotypes内声明。去
	// 
	// 同时设置FakeImportC和go115UsesCgo是一个错误。
	go115UsesCgo bool

	// 如果设置了跟踪，则会将调试跟踪打印到标准输出。
	Trace bool

	// 如果出错！=nil，在类型检查期间发现的每个错误
	// 都会调用它；err有动态类型错误。
	// 次要错误（例如，枚举无效递归类型声明中涉及的所有类型
	// ）具有以“\t”字符开头的
	// 错误字符串。
	// 如果Error==nil，类型检查将停止，并发现第一个
	// 错误。
	Error func(err error)

	// 进口商用于进口来自
	// 进口报关单的包裹。
	// 如果安装的导入程序实现ImporterFrom，则类型
	// 检查器将调用ImportFrom而不是Import。
	// 如果需要导入程序，则类型检查器会报告错误
	// 但未安装任何导入程序。
	Importer Importer

	// 如果尺寸！=无，它为不安全的包提供大小调整功能。
	// 否则使用SizesFor（“gc”、“amd64”）。
	Sizes Sizes

	// 如果设置了DisableUnusedImportCheck，则不检查包
	// 是否有未使用的导入。
	DisableUnusedImportCheck bool
}

func srcimporter_setUsesCgo(conf *Config) {
	conf.go115UsesCgo = true
}

// Info保存类型检查包的结果类型信息。
// 只收集提供地图的信息。
// 如果包有类型错误，收集的信息可能不完整。
type Info struct {
	// 类型将表达式映射到它们的类型，对于常量
	// 表达式，也映射它们的值。
	// 省略了无效表达式。
	// 
	// 对于表示内置
	// 函数的标识符（可能带括号），记录的签名是特定于调用站点的：
	// 如果调用结果不是常数，记录的类型是
	// 特定于参数的签名。否则，记录的类型
	// 无效。
	// 
	// 类型映射不记录每个标识符的类型，
	// 只允许出现在任意表达式为
	// 的位置。例如，选择器
	// 表达式x.f中的标识符f仅在选择映射中找到，变量声明“var z int”中的
	// 标识符z仅在Defs映射中找到
	// 仅在使用映射中收集表示
	// 中的包的标识符。
	Types map[syntax.Expr]TypeAndValue

	// 实例将表示泛型类型或函数的标识符映射到它们的
	// 类型参数和实例化类型。
	// 
	// 例如，实例将把类型
	// 实例化T[int，string]中“T”的标识符映射到类型参数[int，string]和
	// 生成的实例化*命名类型。给定一个泛型函数
	// func F[a any]（a），实例将把调用
	// expression F（int（1））中的'F'标识符映射到推断的类型参数[int]，并生成
	// instanced*签名。
	// 
	// 不变量：实例化使用[id]。带有实例[id]的Type（）。TypeArgs 
	// 结果相当于实例[id]。类型
	Instances map[*syntax.Name]Instance

	// Defs将标识符映射到它们定义的对象（包括
	// 包名、点）点导入和空白“”标识符）。
	// 对于不表示对象的标识符（例如，包子句中的包名
	// 或
	// 类型开关头的符号变量t:=x.（类型），相应的对象为零。
	// 
	// 对于嵌入式字段，Defs返回它定义的字段*Var。
	// 
	// 不变量：Defs[id]==nil | | Defs[id]。Pos（）==id.Pos（）
	Defs map[*syntax.Name]Object

	// 使用将标识符映射到它们所表示的对象。
	// 
	// 对于嵌入式字段，使用返回它所表示的*类型名。
	// 
	// 不变量：使用[id]。Pos（）！=id.Pos（）
	Uses map[*syntax.Name]Object

	// 隐式地将节点映射到其隐式声明的对象（如果有）。
	// 可能会出现以下节点和对象类型：
	// 
	// 节点声明的对象
	// 
	// /*语法。ImportDecl*PkgName用于不带重命名的导入
	// /*语法。CaseClause type specific*Var用于每个类型开关case子句（包括默认值）
	// /*语法。字段匿名参数*Var（包括未命名的结果）
	// 
	Implicits map[syntax.Node]Object

	// 选择将选择器表达式（不包括限定标识符）
	// 映射到它们相应的选择。
	Selections map[*syntax.SelectorExpr]*Selection

	// 作用域映射语法。节点到它们定义的范围。包作用域不是
	// 与特定节点关联，而是与属于包的所有文件关联。
	// 因此，可以在类型检查的包对象中找到包范围。
	// 作用域嵌套，宇宙作用域是最外层的作用域，包含
	// 包作用域，其中包含（一个或多个）文件作用域，其中包含
	// 函数作用域，而函数作用域又包含语句和函数文字作用域。
	// 注意，即使在包
	// 作用域中声明了包级函数，函数作用域也嵌入到包含函数声明的文件
	// 的文件作用域中。
	// 
	// 作用域中可能会出现以下节点类型：
	// 
	// /*语法。文件
	// /*语法。FuncType 
	// /*语法。TypeDecl 
	// /*语法。BlockStmt 
	// /*语法。IfStmt 
	// /*语法。SwitchStmt 
	// /*语法。case子句
	// /*语法。CommClause 
	// /*语法。ForStmt 
	// 
	Scopes map[syntax.Node]*Scope

	// InitOrder是按
	// 必须执行的顺序列出的包级初始值设定项。引用
	// 初始化依赖项相关变量的初始值设定项以拓扑顺序出现，其他变量以源顺序出现
	// 。没有初始化表达式的变量不会显示在此列表中。
	InitOrder []*Initializer
}

// TypeOf返回表达式e的类型，如果找不到则返回nil。
// 先决条件：填充类型、用法和定义映射。
// 
func (info *Info) TypeOf(e syntax.Expr) Type {
	if t, ok := info.Types[e]; ok {
		return t.Type
	}
	if id, _ := e.(*syntax.Name); id != nil {
		if obj := info.ObjectOf(id); obj != nil {
			return obj.Type()
		}
	}
	return nil
}

// ObjectOf返回由指定id表示的对象，如果找不到则返回nil。
// 
// 如果id是嵌入式结构字段，ObjectOf返回它定义的字段（*Var）
// 而不是它使用的类型（*TypeName）。wen dang
// 
func (info *Info) ObjectOf(id *syntax.Name) Object {
	if obj := info.Defs[id]; obj != nil {
		return obj
	}
	return info.Uses[id]
}

// TypeAndValue报告相应表达式的类型和值（对于常量）
// 。
type TypeAndValue struct {
	mode  operandMode
	Type  Type
	Value constant.Value
}

// IsVoid报告相应的表达式
// 是否是没有结果的函数调用。
func (tv TypeAndValue) IsVoid() bool {
	return tv.mode == novalue
}

// IsType报告相应的表达式是否指定了类型。
func (tv TypeAndValue) IsType() bool {
	return tv.mode == typexpr
}

// IsBuiltin报告相应的表达式是否表示
// a（可能带括号）内置函数。
func (tv TypeAndValue) IsBuiltin() bool {
	return tv.mode == builtin
}

// IsValue报告相应的表达式是否为值。
// 内置项不被视为值。常量值具有非-
// nil值。
func (tv TypeAndValue) IsValue() bool {
	switch tv.mode {
	case constant_, variable, mapindex, value, nilvalue, commaok, commaerr:
		return true
	}
	return false
}

// IsNil报告相应的表达式是否表示
// predeclared值nil。根据上下文的不同，它可能是
// 给定的类型与UntypedNil不同。
func (tv TypeAndValue) IsNil() bool {
	return tv.mode == nilvalue
}

// Addressable报告相应的表达式
// 是否可寻址(https:
func (tv TypeAndValue) Addressable() bool {
	return tv.mode == variable
}

// Assignable报告相应的表达式
// 是否可赋值（提供正确类型的值）。
func (tv TypeAndValue) Assignable() bool {
	return tv.mode == variable || tv.mode == mapindex
}

// HasOk报告对应的表达式是否可能是
// 用于逗号ok赋值的rhs。
func (tv TypeAndValue) HasOk() bool {
	return tv.mode == commaok || tv.mode == mapindex
}

// 实例报告类型和
// 函数实例化的类型参数和实例化类型。对于类型实例化，类型将是动态
// type*Named。对于函数实例化，类型将是动态类型
// /*签名。
type Instance struct {
	TypeArgs *TypeList
	Type     Type
}

// 初始值设定项描述了多值初始化表达式的包级变量，或
// 情况下的变量列表，以及相应的初始化
// 表达式。
type Initializer struct {
	Lhs []*Var // var Lhs=Rhs 
	Rhs syntax.Expr
}

func (init *Initializer) String() string {
	var buf bytes.Buffer
	for i, lhs := range init.Lhs {
		if i > 0 {
			buf.WriteString(", ")
		}
		buf.WriteString(lhs.Name())
	}
	buf.WriteString(" = ")
	syntax.Fprint(&buf, init.Rhs, syntax.ShortForm)
	return buf.String()
}

// Check type检查一个包，并返回结果包对象和第一个错误（如果有）。此外，如果信息！=nil，Check填充Info结构中每个
// 的非nil映射。
// 
// 如果没有出现错误，则该包被标记为完整，否则为
// 不完整。请参阅配置。存在
// 错误时控制行为的错误。
// 
// 该包由*语法列表指定。文件和相应的
// 文件集，以及该包标识的包路径。
// 干净路径不能为空或点（“.”）。
func (conf *Config) Check(path string, files []*syntax.File, info *Info) (*Package, error) {
	pkg := NewPackage(path, "")
	return pkg, NewChecker(conf, pkg, info).Files(files)
}

// AssertableTo报告是否可以将V类型的值断言为T类型。
// 
// 在两种情况下，AssertableTo的行为是未定义的：
// /-如果V是通用接口；i、 例如，如果T是非标准泛型
// 中的类型约束。newAssertableTo抑制无效类型的错误，因此我们需要特殊的
func AssertableTo(V *Interface, T Type) bool {
	// Checker，则该接口只能用作Go代码
	// 处理。
	if T.Underlying() == Typ[Invalid] {
		return false
	}
	return (*Checker)(nil).newAssertableTo(V, T) == nil
}

// AssignableTo报告类型V的值是否可分配给类型T的变量
// 
// 
// 如果V或T是未实例化的
// 泛型，则AssignableTo的行为未定义。
func AssignableTo(V, T Type) bool {
	x := operand{mode: value, typ: V}
	ok, _ := x.assignableTo(nil, T, nil) // 非常量x不需要检查
	return ok
}

// 可转换为报告类型V的值是否可转换为
// 类型T。
// 
// 如果V或T是非标准
// 泛型，则可转换为的行为未定义。
func ConvertibleTo(V, T Type) bool {
	x := operand{mode: value, typ: V}
	return x.convertibleTo(nil, T, nil) // 检查非常量x不需要
}

// Implements报告类型V是否实现了接口T。
// 
// 如果V是未实例化的泛型
// 类型，则实现的行为未定义。
func Implements(V Type, T *Interface) bool {
	if T.Empty() {
		// 所有类型（即使是Typ[Invalid]）都实现空接口。
		return true
	}
	// 检查程序。实现抑制无效类型的错误，因此我们需要特殊的
	// 处理。
	if V.Underlying() == Typ[Invalid] {
		return false
	}
	return (*Checker)(nil).implements(V, T) == nil
}

// 相同报告x和y是否为相同类型。
// 忽略签名类型的接收者。
func Identical(x, y Type) bool {
	return identical(x, y, true, nil)
}

// IdenticalIgnoreTags报告如果忽略标记，x和y是否为相同类型。
// 忽略签名类型的接收者。
func IdenticalIgnoreTags(x, y Type) bool {
	return identical(x, y, false, nil)
}
